package com.monkeyk.os.config;

import com.monkeyk.os.oauth.shiro.OAuth2Filter;
import com.monkeyk.os.oauth.shiro.OAuth2JdbcRealm;
import com.monkeyk.os.oauth.shiro.OAuth2SubjectFactory;
import com.monkeyk.os.service.OAuthRSService;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.web.filter.DelegatingFilterProxy;

import javax.servlet.Filter;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by Mr.Yangxiufeng on 2017/12/5.
 * Time:14:52
 * ProjectName:oauth2-shiro
 */
@Configuration
public class ShiroConfig {
    /**
     * <p>凭证(access_token)的匹配使用直接比较是否相等</p>
     *
     * @return
     */
    @Bean
    public CredentialsMatcher getCredentialsMatcher() {
        SimpleCredentialsMatcher credentialsMatcher = new SimpleCredentialsMatcher();
        return credentialsMatcher;
    }

    /**
     * 配置自定义的权限登录器
     *
     * @return
     */
    @Bean(name = "jdbcRealm")
    public OAuth2JdbcRealm getRealm(@Qualifier("dataSource") DataSource dataSource, @Qualifier("oAuthRSService") OAuthRSService rsService) {
        OAuth2JdbcRealm realm = new OAuth2JdbcRealm();
        realm.setCredentialsMatcher(getCredentialsMatcher());
        realm.setDataSource(dataSource);
        realm.setRsService(rsService);
        realm.setPermissionsLookupEnabled(true);
        return realm;
    }

    @Bean
    public OAuth2SubjectFactory getOAuth2SubjectFactory() {
        OAuth2SubjectFactory auth2SubjectFactory = new OAuth2SubjectFactory();
        return auth2SubjectFactory;
    }

    /**
     * 配置核心安全事务管理器
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(@Qualifier("jdbcRealm") OAuth2JdbcRealm jdbcRealm) {
        System.err.println("--------------shiro已经加载----------------");
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(jdbcRealm);
        manager.setCacheManager(getMemoryConstrainedCacheManager());
        manager.setSubjectFactory(getOAuth2SubjectFactory());
        return manager;
    }

    /**
     * 使用基于内存的缓存 SHIRO 相关数据
     *
     * @return
     */
    @Bean
    public MemoryConstrainedCacheManager getMemoryConstrainedCacheManager() {
        MemoryConstrainedCacheManager cacheManager = new MemoryConstrainedCacheManager();
        return cacheManager;
    }

    /**
     * 对于每一个对外提供的Resource(资源), 都需要在此定义, 每个资源对应不同的URL pattern.
     * 且有唯一的 resource id,  具体的拦截处理见 OAuth2Filter.java 类
     * 可根据实际需要 进行扩展
     */
    @Bean(name = "auth2Filter")
    public OAuth2Filter getOAuth2Filter(@Qualifier("oAuthRSService") OAuthRSService rsService) {
        OAuth2Filter oAuth2Filter = new OAuth2Filter();
        oAuth2Filter.setResourceId("os-resource");
        oAuth2Filter.setRsService(rsService);
        return oAuth2Filter;
    }

    @Bean(name = "mobileOauth2Filter")
    public OAuth2Filter getMobileOauth2Filter(@Qualifier("oAuthRSService") OAuthRSService rsService) {
        OAuth2Filter oAuth2Filter = new OAuth2Filter();
        oAuth2Filter.setResourceId("mobile-resource");
        oAuth2Filter.setRsService(rsService);
        return oAuth2Filter;
    }

    @Bean
    public FilterRegistrationBean delegatingFilterProxy(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        proxy.setTargetBeanName("shiroFilter");
        filterRegistrationBean.setFilter(proxy);
        return filterRegistrationBean;
    }

    /**
     * Shiro的过滤器链
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") DefaultWebSecurityManager securityManager, @Qualifier("auth2Filter") OAuth2Filter auth2Filter, @Qualifier("mobileOauth2Filter") OAuth2Filter mobileOauth2Filter) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        Map<String, Filter> filterMap = new HashMap<>();
        filterMap.put("oauth", auth2Filter);
        filterMap.put("mOauth", mobileOauth2Filter);
        shiroFilter.setFilters(filterMap);
        /**
         * 默认的登陆访问url
         */
        shiroFilter.setLoginUrl("/login");
        /**
         * 登陆成功后跳转的url
         */
        shiroFilter.setSuccessUrl("/index");
        /**
         * 没有权限跳转的url
         */
        shiroFilter.setUnauthorizedUrl("/unauthorized");
        /**
         * 配置shiro拦截器链
         *
         * anon  不需要认证
         * authc 需要认证
         * user  验证通过或RememberMe登录的都可以
         *
         */
        Map<String, String> hashMap = new HashMap<>();
        hashMap.put("/rs/**", "oauth");
        hashMap.put("/mobile/**", "mOauth");
        hashMap.put("/**", "anon");
        shiroFilter.setFilterChainDefinitionMap(hashMap);
        return shiroFilter;
    }

    /**
     * 保证实现了Shiro内部lifecycle函数的bean执行
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 启用shrio授权注解拦截方式，AOP式方法级权限检查
     */
    @Bean
    @DependsOn(value = "lifecycleBeanPostProcessor") //依赖其他bean的初始化
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        return new DefaultAdvisorAutoProxyCreator();
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
                new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
